package ch.ethz.fcl;

import java.awt.GridLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.highgui.Highgui;

import ch.ethz.fcl.input.Coordinate;
import ch.ethz.fcl.process.polygon.Polygon;
import ch.ethz.fcl.tools.ImageTools;
import ch.ethz.fcl.tools.KeyInteraction;
import ch.ethz.fcl.view.TableView;
import ch.ethz.fcl.view.ViewComponent;

public class RealityCapture {
	private KinectContainer container;

	private ViewComponent contoursView;
	private ViewComponent polyLinesView;
	private ViewComponent polygonsView;

	private TableView tableView;

	private JFrame frame;

	List<MatOfPoint> allContours = null;
	List<MatOfPoint> allPolys = null;
	List<MatOfPoint> allPolygonPoints = null;
	List<Polygon> allPolygons = null;

	Mat histMat;

	public RealityCapture(JFrame frame) {
		this.frame = frame;
		frame.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
					System.exit(0);
				}
			}
		});
	}

	public static void main(String s[]) {
		System.loadLibrary("opencv_java245");

		JFrame frame = new JFrame("Reality projection");
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		RealityCapture app = new RealityCapture(frame);

		app.container = new KinectContainer();
		frame.addKeyListener(new KeyInteraction(app.container));

		app.contoursView = new ViewComponent();
		app.polyLinesView = new ViewComponent();
		app.polygonsView = new ViewComponent();
		app.tableView = new TableView(app.container);

		frame.setLayout(new GridLayout(1, 2));
		// frame.add(app.rawView);
		// frame.add(app.contoursView);
		frame.add(app.polyLinesView);
		frame.add(app.polygonsView);
		frame.add(app.tableView);

		frame.setLocation(0, 0);

		frame.pack();
		frame.setVisible(true);
		app.run();
	}

	void run() {
		try {
			int count = 0;
			int similarImgCount = 0;
			boolean reset = true;

			while (true) {
				count++;
				if (Constant.DEBUG)
					if (count == 10)
						System.exit(0);

				if (Constant.KINECT_AVAI) {
					container.getDepthImage().update();
					container.getTableDepthMap().saveHistDepthMap();
				} else
					container.getDepthImage().updateFromDepthMap(
							container.getTableDepthMap().getHistDepthMap(),
							container.getTableDepthMap().getHeightMap());

				Mat imageMat = container.getDepthImage().getAllLayerImageMat();

				if (count > 1) {
					Mat preImageMat = Coordinate.crop(histMat);

					Mat curImageMat = Coordinate.crop(imageMat);

					float psnr = (float) getPSNR(preImageMat, curImageMat);

					if (psnr >= 45)
						similarImgCount++;
					else
						similarImgCount = 0;

					if (count < 40
							|| (similarImgCount > 2 && similarImgCount < 15))
						reset = true;

					// System.out.println(count + " " + psnr + " "
					// + similarImgCount + " " + reset);
				}

				if (reset) {
					reset();
					reset = false;
				}

				if (count == 40 || (count > 40 && similarImgCount == 15))
					sendUDP(getCoordinatesMsg());
				/*if (count == 40 || (count > 40 && similarImgCount == 10))
					sendUDP("N"+getCoordinatesMsg());
				else if(count > 40 && similarImgCount > 10)
					sendUDP("R"+getCoordinatesMsg());*/

				histMat = container.getDepthImage().getAllLayerImageMat();

				Mat contours = ImageTools.drawPolys(histMat, allContours);
				contoursView.update(Coordinate.drawTable(KeyInteraction
						.drawPoint(contours)));
				contoursView.repaint();

				getCoordinatesMsg();

				frame.pack();

				// System.out.println("time interval "
				// + (System.currentTimeMillis() - currentTime) / 1000.0f);
				// currentTime = System.currentTimeMillis();
			}
		} catch (Exception e) {
		}
	}

	private void reset() {
		allContours = new ArrayList<MatOfPoint>();
		allPolys = new ArrayList<MatOfPoint>();
		allPolygonPoints = new ArrayList<MatOfPoint>();
		allPolygons = new ArrayList<Polygon>();

		Mat imageMat = null;
		for (int k = 0; k < Constant.HEIGHTS.length; k++) {
			if (Constant.DEBUG)
				if (k != 0)
					continue;

			imageMat = container.getDepthImage().getImageMats()[k];

			imageMat = container.getDepthImageProcess().process(imageMat);

			allContours.addAll(container.getDepthImageProcess().getContours());
			allPolys.addAll(container.getDepthImageProcess().getPolys());

			List<MatOfPoint> polygons = container.getPolygonFit().processPolys(
					Constant.HEIGHTS[k],
					container.getDepthImageProcess().getPolys());
			allPolygonPoints.addAll(polygons);

			List<Polygon> validatedPolygons = container.getPolygonFit()
					.getPolygons();
			allPolygons.addAll(validatedPolygons);
		}

		imageMat = container.getDepthImage().getAllLayerImageMat();

		Mat polys = ImageTools.drawPolys(imageMat, allPolys);
		polyLinesView.update(KeyInteraction.drawPoint(polys));
		polyLinesView.repaint();

		Mat polygonMat = ImageTools.drawPolys(imageMat, allPolygonPoints);
		for (int i = 0; i < allPolygons.size(); i++) {
			Polygon polygon = allPolygons.get(i);
			if (polygon == null)
				continue;

			if (polygon.getCenter() == null)
				continue;

			Core.putText(polygonMat, polygon.getHeight() + "",
					polygon.getCenter(), Core.FONT_HERSHEY_PLAIN, 1,
					new Scalar(1, 0, 0));
		}

		if (Constant.DEBUG)
			Highgui.imwrite("res/valuelab/result.png", polygonMat);

		polygonsView.update(KeyInteraction.drawPoint(polygonMat));
		polygonsView.repaint();

		tableView.reset();
		tableView.addPolygons(allPolygons);
		tableView.repaint();
	}

	// http://docs.opencv.org/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html#videoinputpsnrmssim
	// PSNR method for comparing two images
	protected double getPSNR(Mat img1, Mat img2) {
		// convert to 8 bit then compare
		Mat img1_8 = new Mat(img1.size(), CvType.CV_8UC1);
		img1.convertTo(img1_8, CvType.CV_8UC1, 0.00390625);
		Mat img2_8 = new Mat(img2.size(), CvType.CV_8UC1);
		img2.convertTo(img2_8, CvType.CV_8UC1, 0.00390625);

		// Mat img1_8 = img1, img2_8 = img2;

		Mat sl = new Mat();
		Core.absdiff(img1_8, img2_8, sl);
		Core.multiply(sl, sl, sl);

		Scalar s = Core.sumElems(sl);
		double sse = s.val[0] + s.val[1] + s.val[2];
		if (sse < Math.exp(-10))
			return 100;
		else {
			double mse = sse / (double) (img1_8.channels() * img1_8.total());
			double psnr = 10.0 * Math.log10(255 * 255 / mse);
			return psnr;
		}
	}

	private String getCoordinatesMsg() {
		if (allPolygons == null)
			return "";

		float width = 1000, length = 1000;// table length and width in mm
		String coordinates = "/";
		for (int i = 0; i < allPolygons.size(); i++) {
			Polygon polygon = allPolygons.get(i);

			if (polygon == null)
				continue;

			for (int j = 0; j < polygon.getPoints().size(); j++) {
				Point p = polygon.getPoints().get(j);
				float x = (float) p.x, y = (float) p.y;

				double[] imagePos = Coordinate
						.groundImagePosition(
								x,
								y,
								container.getTableDepthMap()
										.getGroundDepthMap()[Coordinate.HEIGHT / 2][Coordinate.WIDTH / 2],
								polygon.getHeight());

				double[] tablePos = Coordinate.getPositionOnTable(imagePos[0],
						imagePos[1], length, width);

				coordinates += (int) tablePos[0] + "," + (int) tablePos[1]
						+ "," + polygon.getHeight();

				if (j != polygon.getPoints().size() - 1)
					coordinates += ";";
				else
					coordinates += "/";
			}
		}
		return coordinates;
	}
	
	public void sendUDP(/*Vector<Tri> tris*/ String sentence)
	{
		System.out.println(sentence);
		BufferedReader inFromUser =
		         new BufferedReader(new InputStreamReader(System.in));
		DatagramSocket clientSocket;
		try {
			clientSocket = new DatagramSocket();
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		InetAddress IPAddress;
		try {
			IPAddress = InetAddress.getByName("localhost");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		byte[] sendData = new byte[1024];
		byte[] receiveData = new byte[1024];
		
		
		
		sendData = sentence.getBytes();
		DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 9876);
		      try {
				clientSocket.send(sendPacket);
				//System.out.println("sending...");
			} catch (IOException e) {
				System.out.println("Error sending UDP message");
				e.printStackTrace();
			}
		      
		clientSocket.close();
	}
}
